package dao

import (
	"a-common/constant"
	dom "a-common/domain"
	"a-system/domain/entity"
	"errors"
	"strconv"
	"strings"
	"sync"
	"time"

	"xorm.io/builder"
)

var (
	userDaoTemp *UserDao
	userDaoOnce sync.Once
)

type UserDao struct{}

func NewSysUserDao() *UserDao {
	userDaoOnce.Do(func() {
		userDaoTemp = &UserDao{}
	})
	return userDaoTemp
}

// List 分页
func (m *UserDao) List(args *entity.UserDto, isExport bool) (dom.PageVo, error) {
	// 构建查询条件
	query := pgCli.Table(&entity.SysUser{}).Join("LEFT", "sys_dept", "sys_dept.dept_id=sys_user.dept_id").Where("1 = 1")
	if args.UserName != "" {
		query.And("sys_user.user_name = ?", args.UserName)
	}
	if args.Phonenumber != "" {
		query.And("sys_user.phonenumber = ?", args.Phonenumber)
	}
	if args.Status != "" {
		query.And("sys_user.status = ?", args.Status)
	}
	if args.Params.BeginTime != "" && args.Params.EndTime != "" {
		query.And("sys_user.created_at BETWEEN ? AND ?", args.Params.BeginTime, args.Params.EndTime)
	}
	if args.DeptId != 0 {
		// 查询与该部门ID关联的所有ID
		departmentIds, _ := NewSysDeptDao().GetRelatedDepartmentIds(args.DeptId)
		query.In("sys_user.dept_id", departmentIds)
	}
	if args.PageSize != 0 {
		query.Limit(args.PageSize, (args.PageNum-1)*args.PageSize)
	}
	// 查询
	var vPage dom.PageVo
	if isExport {
		var rows []entity.SysUserExportVo
		total, err := query.Asc("sys_user.user_id").FindAndCount(&rows)
		if err != nil {
			return vPage, err
		}
		vPage.Rows = rows
		vPage.Total = total
	} else {
		var rows []entity.SysUserPageVo
		total, err := query.Asc("sys_user.user_id").FindAndCount(&rows)
		if err != nil {
			return vPage, err
		}
		vPage.Rows = rows
		vPage.Total = total
	}
	vPage.PageNum = args.PageNum
	vPage.PageSize = args.PageSize
	return vPage, nil
}

// Get 查
func (m *UserDao) Get(id int64) (entity.SysUserPageVo, error) {
	var v entity.SysUserPageVo
	_, err := pgCli.Table(&entity.SysUser{}).Join(
		"left",
		"sys_dept",
		"sys_dept.dept_id = sys_user.dept_id",
	).Join(
		"left",
		"sys_user_role",
		"sys_user_role.user_id = sys_user.user_id",
	).Join(
		"left",
		"sys_role",
		"sys_role.role_id = sys_user_role.role_id",
	).ID(id).Get(&v)
	return v, err
}

// Del 删
func (m *UserDao) Del(ids string) (int64, error) {
	return pgCli.In("user_id", strings.Split(ids, `,`)).Delete(&entity.SysUser{})
}

// Add 增
func (m *UserDao) Add(args *entity.UserDto) error {
	session := pgCli.NewSession()
	defer session.Close()
	if err := session.Begin(); err != nil {
		return err
	}

	// 加密
	pwd, err := cryptUtils.EnCrypt(args.Password)
	if err != nil {
		return err
	}

	var sysUser entity.SysUser
	sysUser.UserName = args.UserName
	sysUser.NickName = args.NickName
	sysUser.DeptId = args.DeptId
	sysUser.Phonenumber = args.Phonenumber
	sysUser.Email = args.Email
	sysUser.Sex = args.Sex
	sysUser.Password = pwd
	sysUser.Status = args.Status
	sysUser.CreatedBy = args.CreatedBy
	sysUser.UpdatedBy = args.CreatedBy
	sysUser.Remark = args.Remark

	if _, err := pgCli.Insert(&sysUser); err != nil {
		return err
	}
	if sysUser.UserId == 0 {
		return errors.New("新增失败")
	}

	// 新增用户与角色管理
	var userRoles []entity.SysUserRole
	for _, item := range args.RoleIds {
		userRoles = append(userRoles, entity.SysUserRole{UserId: sysUser.UserId, RoleId: item})
	}
	if _, err := session.Insert(&userRoles); err != nil {
		return err
	}

	// 新增用户与岗位管理
	var userPosts []entity.SysUserPost
	for _, item := range args.PostIds {
		userPosts = append(userPosts, entity.SysUserPost{UserId: sysUser.UserId, PostId: item})
	}
	if _, err := session.Insert(&userPosts); err != nil {
		return err
	}

	return session.Commit()
}

// Edit 改
func (m *UserDao) Edit(args *entity.UserDto) error {
	session := pgCli.NewSession()
	defer session.Close()
	if err := session.Begin(); err != nil {
		return err
	}

	if len(args.RoleIds) > 0 {
		// 删除用户与角色关联
		if _, err := session.Where("user_id = ?", args.UserId).Unscoped().Delete(&entity.SysUserRole{}); err != nil {
			return err
		}
		// 新增用户与角色管理
		var userRoles []entity.SysUserRole
		for _, item := range args.RoleIds {
			userRoles = append(userRoles, entity.SysUserRole{UserId: args.UserId, RoleId: item})
		}
		if _, err := session.Insert(&userRoles); err != nil {
			return err
		}
	}

	if len(args.PostIds) > 0 {
		// 删除用户与岗位关联
		if _, err := session.Where("user_id = ?", args.UserId).Unscoped().Delete(&entity.SysUserPost{}); err != nil {
			return err
		}
		// 新增用户与岗位管理
		var userPosts []entity.SysUserPost
		for _, item := range args.PostIds {
			userPosts = append(userPosts, entity.SysUserPost{UserId: args.UserId, PostId: item})
		}
		if _, err := session.Insert(&userPosts); err != nil {
			return err
		}
	}

	var v entity.SysUser
	v.NickName = args.NickName
	v.DeptId = args.DeptId
	v.Phonenumber = args.Phonenumber
	v.Email = args.Email
	v.Sex = args.Sex
	v.Status = args.Status
	v.UpdatedBy = args.UpdatedBy
	v.Remark = args.Remark

	if _, err := pgCli.ID(args.UserId).Update(&v); err != nil {
		return err
	}

	return session.Commit()
}

// Get 查
func (m *UserDao) GetByUserName(id int64) (entity.SysUserPageVo, error) {
	var v entity.SysUserPageVo
	_, err := pgCli.Table(&entity.SysUser{}).Join(
		"left",
		"sys_dept",
		"sys_dept.dept_id = sys_user.dept_id",
	).Join(
		"left",
		"sys_user_role",
		"sys_user_role.user_id = sys_user.user_id",
	).Join(
		"left",
		"sys_role",
		"sys_role.role_id = sys_user_role.role_id",
	).ID(id).Get(&v)
	return v, err
}

// EditByIpAndTime 更新IP和登陆时间
func (m *UserDao) EditByIpAndTime(userId int64, ip string, time time.Time, token string) error {
	_, err := pgCli.Table(&entity.SysUser{}).ID(userId).NoAutoTime().Update(
		map[string]interface{}{
			"login_ip":    ip,
			"login_date":  time,
			"login_token": token,
		})
	return err
}

// EditByAvatar 更新用户头像路径
func (m *UserDao) EditByAvatar(userId int64, url string) error {
	_, err := pgCli.Table(&entity.SysUser{}).ID(userId).Update(
		&entity.SysUser{
			Avatar: url,
		})
	return err
}

// ChangeStatus 改变状态
func (m *UserDao) ChangeStatus(userId int64, status string, updateBy string) (int64, error) {
	var v entity.SysUser
	v.Status = status
	v.UpdatedBy = updateBy
	i, err := pgCli.ID(userId).Update(&v)
	return i, err
}

// UpdatePwd 更新密码
func (m *UserDao) UpdatePwd(userId int64, password string, updateBy string) (int64, error) {
	password, err := cryptUtils.EnCrypt(password)
	if err != nil {
		return 0, err
	}
	return pgCli.ID(userId).Update(&entity.SysUser{
		Password: password,
		BaseStruct: dom.BaseStruct{
			UpdatedBy: updateBy,
		},
	})
}

// GetAllocatedList 根据条件分页查询已配用户角色列表
func (m *UserDao) GetAllocatedList(args *entity.UserDto) (dom.PageVo, error) {
	var vPage dom.PageVo
	var rows []entity.SysUserVo
	var total int64

	// 构建查询条件
	query := pgCli.Table(&entity.SysUser{}).Cols(
		"sys_user.user_id",
		"sys_user.dept_id",
		"sys_user.user_name",
		"sys_user.nick_name",
		"sys_user.email",
		"sys_user.phonenumber",
		"sys_user.phonenumber",
		"sys_user.status",
		"sys_user.created_at",
	).Join(
		"left",
		"sys_dept",
		"sys_dept.dept_id = sys_user.dept_id",
	).Join(
		"left",
		"sys_user_role",
		"sys_user_role.user_id = sys_user.user_id",
	).Join(
		"left",
		"sys_role",
		"sys_role.role_id = sys_user_role.role_id",
	).Where("1 = 1")
	if args.RoleId != 0 {
		query.And("sys_role.role_id = ?", args.RoleId)
	}
	if args.UserName != "" {
		query.And("sys_user.user_name like ?", args.UserName+"%")
	}
	if args.Status != "" {
		query.And("sys_user.status = ?", args.Status)
	}
	if args.Phonenumber != "" {
		query.And("sys_user.phonenumber like ?", args.Phonenumber+"%")
	}

	// 查询
	total, err := query.Limit(args.PageSize, (args.PageNum-1)*args.PageSize).Asc("sys_user.user_id").FindAndCount(&rows)
	if err != nil {
		return vPage, err
	}

	vPage.PageNum = args.PageNum
	vPage.PageSize = args.PageSize
	vPage.Rows = rows
	vPage.Total = total
	return vPage, err
}

// GetUnallocatedList 根据条件分页查询未分配用户角色列表
func (m *UserDao) GetUnallocatedList(args *entity.UserDto, userIds []int64) (dom.PageVo, error) {
	var vPage dom.PageVo
	var rows []entity.SysUserVo
	var total int64

	// 构建查询条件
	query := pgCli.Table(&entity.SysUser{}).Cols(
		"sys_user.user_id",
		"sys_user.dept_id",
		"sys_user.user_name",
		"sys_user.nick_name",
		"sys_user.email",
		"sys_user.phonenumber",
		"sys_user.phonenumber",
		"sys_user.status",
		"sys_user.created_at",
	).Join(
		"left",
		"sys_dept",
		"sys_dept.dept_id = sys_user.dept_id",
	).Join(
		"left",
		"sys_user_role",
		"sys_user_role.user_id = sys_user.user_id",
	).Join(
		"left",
		"sys_role",
		"sys_role.role_id = sys_user_role.role_id",
	).Where("1 = 1").And(builder.Neq{"sys_role.role_id": args.RoleId}.Or(builder.IsNull{"sys_role.role_id"}))
	if len(userIds) > 0 {
		query.NotIn("sys_user.user_id", userIds)
	}
	if args.UserName != "" {
		query.And("sys_user.user_name like ?", args.UserName+"%")
	}
	if args.Phonenumber != "" {
		query.And("sys_user.phonenumber like ?", args.Phonenumber+"%")
	}

	// 查询
	total, err := query.Limit(args.PageSize, (args.PageNum-1)*args.PageSize).Asc("sys_user.user_id").FindAndCount(&rows)
	if err != nil {
		return vPage, err
	}

	vPage.PageNum = args.PageNum
	vPage.PageSize = args.PageSize
	vPage.Rows = rows
	vPage.Total = total
	return vPage, err
}

// GetPostIdsByUserId 获取用户的部门ID
func (m *UserDao) GetPostIdsByUserId(uId int64) ([]int64, error) {
	var ids []int64
	err := pgCli.Table(&entity.SysUserPost{}).Cols("post_id").Where("user_id = ?", uId).Find(&ids)
	return ids, err
}

// GetRoleIdsByUserId 通过用户ID获取角色ID列表
func (m *UserDao) GetRoleIdsByUserId(userId int64) ([]int64, error) {
	var ids []int64
	err := pgCli.Table(&entity.SysUserRole{}).Cols("role_id").Where("user_id = ?", userId).Find(&ids)
	return ids, err
}

// GetUserIdsByRoleId 通过角色ID获取用户ID列表
func (m *UserDao) GetUserIdsByRoleId(roleId int64) ([]int64, error) {
	var ids []int64
	err := pgCli.Table(&entity.SysUserRole{}).Cols("user_id").Where("role_id = ?", roleId).Find(&ids)
	return ids, err
}

// CheckUserDataScope 校验登陆用户是否有数据权限
func (m *UserDao) CheckUserDataScope(formDeptId int64, toDeptId int64, uId int64) (bool, error) {
	// 根用户具有全部权限
	if uId == constant.ADMIN_ID {
		return true, nil
	}

	loginUserVo, err := m.Get(uId)
	if err != nil {
		return false, nil
	}

	// 查询角色, 判断该角色可操作的数据范围
	roleVos, err := NewSysRoleDao().GetRoleByUserId(loginUserVo.UserId)
	if err != nil {
		return false, nil
	}

	// 判断是否有修改原部门的权限
	auth1 := false
	if formDeptId != 0 {
		for _, item := range roleVos {
			auth, err := m.checkUserDataScope(item, loginUserVo, formDeptId)
			if err != nil {
				return false, err
			}
			if auth {
				auth1 = auth
				break
			}
		}
	}
	if !auth1 && formDeptId != 0 {
		return false, err
	}

	// 判断是否有修改到部门的权限
	if toDeptId == 0 {
		return true, nil
	} else {
		for _, item := range roleVos {
			auth, err := m.checkUserDataScope(item, loginUserVo, formDeptId)
			if err != nil {
				return false, err
			}
			if auth {
				return true, nil
			}
		}
		return false, nil
	}
}

// CheckUserDataScope 校验登陆用户是否有数据权限
func (m *UserDao) checkUserDataScope(roleVo entity.SysRoleVo, loginUserVo entity.SysUserPageVo, deptId int64) (bool, error) {
	if roleVo.DataScope == "1" {
		// 全部数据
		return true, nil
	} else if roleVo.DataScope == "2" {
		// 自定义数据
		deptIds, err := NewSysDeptDao().GetDeptIdsByRoleId(roleVo.RoleId, false)
		if err != nil {
			return false, err
		}
		for _, id := range deptIds {
			if id == deptId {
				return true, nil
			}
		}
	} else if roleVo.DataScope == "3" {
		// 本部门数据
		if loginUserVo.DeptId == deptId {
			return true, nil
		}
	} else if roleVo.DataScope == "4" {
		// 本部门及以下数据
		if loginUserVo.DeptId == deptId {
			return true, nil
		}
		deptIds, err := NewSysDeptDao().GetRelatedDepartmentIds(loginUserVo.DeptId)
		if err != nil {
			return false, err
		}
		for _, id := range deptIds {
			if id == deptId {
				return true, nil
			}
		}
	} else if roleVo.DataScope == "5" {
		// 仅自己数据
		if loginUserVo.UserId == deptId {
			return true, nil
		}
	}
	return false, nil
}

// CheckNickNameUnique 校验用户昵称是否唯一
func (m *UserDao) CheckNickNameUnique(nikeName string, userId int64) (bool, error) {
	query := pgCli.Where("1 = 1")
	if userId != 0 {
		query.And("user_id != ?", userId)
	}
	return query.Exist(&entity.SysUser{
		NickName: nikeName,
	})
}

// CheckUserNameUnique 校验用户名称是否唯一
func (m *UserDao) CheckUserNameUnique(userName string, userId int64) (bool, error) {
	query := pgCli.Where("1 = 1")
	if userId != 0 {
		query.And("user_id != ?", userId)
	}
	return query.Exist(&entity.SysUser{
		UserName: userName,
	})
}

// CheckPhoneUnique 校验手机号码是否唯一
func (m *UserDao) CheckPhoneUnique(phonenumber string, userId int64) (bool, error) {
	query := pgCli.Where("1 = 1")
	if userId != 0 {
		query.And("user_id != ?", userId)
	}
	return query.Exist(&entity.SysUser{
		Phonenumber: phonenumber,
	})
}

// CheckEmailUnique 校验email是否唯一
func (m *UserDao) CheckEmailUnique(email string, userId int64) (bool, error) {
	query := pgCli.Where("1 = 1")
	if userId != 0 {
		query.And("user_id != ?", userId)
	}
	return query.Exist(&entity.SysUser{
		Email: email,
	})
}

// InsertUserAuth 用户授权角色
func (m *UserDao) InsertUserAuth(userId int64, roleIds []int64) error {
	session := pgCli.NewSession()
	defer session.Close()
	if err := session.Begin(); err != nil {
		return err
	}

	_, err := session.Delete(&entity.SysUserRole{UserId: userId})
	if err != nil {
		return err
	}

	var userRoles []entity.SysUserRole
	for _, item := range roleIds {
		userRoles = append(userRoles, entity.SysUserRole{UserId: userId, RoleId: item})
	}
	if _, err := session.Insert(&userRoles); err != nil {
		return err
	}

	return session.Commit()
}

// ImportData 导入
func (m *UserDao) ImportData(updateSupport int, importList []entity.SysUserImportVo, createdBy string) (string, error) {
	session := pgCli.NewSession()
	defer session.Close()
	if err := session.Begin(); err != nil {
		return "", err
	}

	successNum := int64(0)
	failureNum := int64(0)
	successMsg := ""
	failureMsg := ""
	msg := ""
	for _, t := range importList {
		if t.UserName == constant.ADMIN {
			return msg, errors.New("不允许操作超级管理员角色")
		}

		// 查询是否存在用户名
		has, err := m.CheckUserNameUnique(t.UserName, 0)
		if err != nil {
			return msg, err
		}

		if t.Status == "正常" {
			t.Status = constant.T
		} else {
			t.Status = constant.F
		}

		if t.Sex == "男" {
			t.Sex = "0"
		} else if t.Sex == "女" {
			t.Sex = "1"
		} else {
			t.Sex = "2"
		}

		// 加密
		pwd, err := cryptUtils.EnCrypt("123456")
		if err != nil {
			return msg, errors.New("不允许操作超级管理员角色")
		}

		var sysUser entity.SysUser
		sysUser.UserName = t.UserName
		sysUser.NickName = t.NickName
		sysUser.DeptId = t.DeptId
		sysUser.Phonenumber = t.Phonenumber
		sysUser.Email = t.Email
		sysUser.Sex = t.Sex
		sysUser.Password = pwd
		sysUser.Status = t.Status
		sysUser.CreatedBy = createdBy
		sysUser.UpdatedBy = createdBy

		if !has {
			if _, err := pgCli.Insert(&sysUser); err != nil {
				return msg, err
			}
			if sysUser.UserId == 0 {
				return msg, errors.New("不允许操作超级管理员角色")
			}
			successNum = successNum + 1
			successMsg = successMsg + "<br/>" + strconv.FormatInt(successNum, 10) + "、账号 " + t.UserName + " 更新成功"
		} else if updateSupport == 1 {
			// 更新
			if _, err := pgCli.Where("user_name = ?", t.UserName).Update(&sysUser); err != nil {
				return msg, err
			}
			successNum = successNum + 1
			successMsg = successMsg + "<br/>" + strconv.FormatInt(successNum, 10) + "、账号 " + t.UserName + " 更新成功"
		} else {
			// 已存在
			failureNum = failureNum + 1
			failureMsg = failureMsg + "<br/>" + strconv.FormatInt(failureNum, 10) + "、账号 " + t.UserName + " 已存在"
		}
	}

	if failureNum > 0 {
		msg = "很抱歉，导入失败！共 " + strconv.FormatInt(failureNum, 10) + " 条数据格式不正确，错误如下：" + failureMsg
	} else {
		msg = "恭喜您，数据已全部导入成功！共 " + strconv.FormatInt(successNum, 10) + " 条，数据如下：" + successMsg
	}

	return msg, session.Commit()
}

// GetSelectList 获取用户下拉列表
func (m *UserDao) GetSelectList() ([]entity.SysUserSelectListVo, error) {
	list := make([]entity.SysUserSelectListVo, 0)
	err := pgCli.Table(&entity.SysUser{}).Cols("user_id", "user_name").Where("status = '0'").Asc("user_id").Find(&list)
	return list, err
}
